﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;



namespace Crocodile.AbstractLanguage.Tree
{

    /// <summary>
    /// List that can handle events when items are added and removed.
    /// </summary>
    /// <typeparam name="T">type of items.</typeparam>
    [DebuggerDisplay("Count = {Count}")]
    [DebuggerTypeProxy(typeof(ObservingListDebugView<>))]
    public abstract class ObservingList<T> : IList<T>
    {
        internal readonly List<T> list =
            new List<T>();


        /// <summary>
        /// Calls when an item has been added to the list.
        /// </summary>
        /// <param name="item">itam that has been added.</param>
        protected virtual void OnItemAdded(T item) {}

        /// <summary>
        /// Calls when an item has been removed from the list.
        /// </summary>
        /// <param name="item">itam that has been added.</param>
        protected virtual void OnItemRemoved(T item) {}


        public T this[int index]
        {
            get
            {
                return list[index];
            }

            set
            {
                OnItemRemoved(list[index]);
                list[index] = value;
                OnItemAdded(value);
            }
        }


        public void Add(T item)
        {
            list.Add(item);
            OnItemAdded(item);
        }


        public void AddRange(IEnumerable<T> collection)
        {
            var itemsToAdd = collection.ToList();
            int cn = list.Count + itemsToAdd.Count;
            if (cn < list.Capacity)
                list.Capacity = cn;
            list.AddRange(itemsToAdd);
            foreach (T item in itemsToAdd)
                OnItemAdded(item);
        }


        public void Insert(int index, T item)
        {
            list.Insert(index,item);
            OnItemAdded(item);
        }

        
        public void InsertRange(int index,IEnumerable<T> collection)
        {
            var itemsToInsert = collection.ToList();
            int cn = list.Count + itemsToInsert.Count;
            if (cn < list.Capacity)
                list.Capacity = cn;
            list.InsertRange(index, itemsToInsert);
            foreach (T item in itemsToInsert)
                OnItemAdded(item);
        }


        public void Sort(Comparison<T> comparison)
        {
            list.Sort(comparison);
        }


        public void RemoveAt(int index)
        {
            OnItemRemoved(list[index]);
            list.RemoveAt(index);
        }


        public bool Remove(T item)
        {
            bool removed = list.Remove(item);
            if (removed)
                OnItemRemoved(item);
            return removed;
        }


        public void RemoveRange(int index, int count)
        {
            for (int i = index + count - 1; i >= index; i--)
                OnItemRemoved(list[i]);
            list.RemoveRange(index, count);
        }


        public void Clear()
        {
            for (int i = list.Count - 1; i >= 0; i--)
                OnItemRemoved(list[i]);
            list.Clear();
        }


        public int IndexOf(T item)
        {
            return list.IndexOf(item);
        }


        public int IndexOf(T item,int index)
        {
            return list.IndexOf(item,index);
        }


        public int IndexOf(T item,int index,int count)
        {
            return list.IndexOf(item, index, count);
        }


        public bool Contains(T item)
        {
            return list.Contains(item);
        }


        public void CopyTo(T[] array)
        {
            list.CopyTo(array);
        }


        public void CopyTo(T[] array, int arrayIndex)
        {
            list.CopyTo(array, arrayIndex);
        }


        public int Count
        {
            get { return list.Count; }
        }


        public IEnumerator<T> GetEnumerator()
        {
            return list.GetEnumerator();
        }


        IEnumerator IEnumerable.GetEnumerator()
        {
            return list.GetEnumerator();
        }


        public bool IsReadOnly
        {
            get { return false; }
        }


        public bool IsNotEmpty
        {
            get { return list.Count > 0; }
        }


        public bool IsEmpty
        {
            get { return list.Count == 0; }
        }
    }


    #region DEBUG HELPER

    /// <summary>
    /// Debug helper.
    /// </summary>
    internal class ObservingListDebugView<T>
    {
        private readonly ObservingList<T> olist;


        public ObservingListDebugView(ObservingList<T> olist)
        {
            this.olist = olist;
        }


        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public Array Items
        {
            get
            {
                if (olist != null && olist.IsNotEmpty)
                    return olist.ToArray();
                else
                    return new T[0];
            }
        }
    }

    #endregion
}
